/*  FSM template - A preprocessor template to construct finite state machines

    Copyright (C) 2013 Dushara Jayasinghe.

    fsm_template is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    fsm_template is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with fsm_template.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
    \brief Template to build finite state machines (FSM)s

    This template uses preprocessor magic to build the FSM. It requires that
    three macros are defined before #including the template:

    1. STATE_LIST
    2. EVENT_LIST
    3. INITIAL_STATE
    4. RETURN_TYPE

    The following example illustrate how they should be defined:

    \code {.c}
    #define STATE_LIST(DEFINE_STATE)    DEFINE_STATE(state_name1)   \
                                        DEFINE_STATE(state_name2)   \
                                        DEFINE_STATE(state_name3)
    \endcode

    The argument for DEFINE_STATE is the name of the state.

    \code {.c}
    #define EVENT_LIST(DEFINE_EVENT, __fsm_internal_arg__)   \
        DEFINE_EVENT(event1,    { int arg; },                       __fsm_internal_arg__ )  \
        DEFINE_EVENT(event2,    { const char* string; int iarg; },  __fsm_internal_arg__ )  \
        DEFINE_EVENT(event3,    { int __dummy__; },                 __fsm_internal_arg__ )
    \endcode

    The DEFINE_EVENT macro arguments are:
        * Event name
        * Event payload (will construct a typedefed struct with the payload).
                        \note the event payload may require atleast 1 argument
                        (C doesn't allow empty structs) hence the __dummy__ in
                        event3).
        * __fsm_internal_arg__ - an opaque argument used to construct the state
                              machine action handlers (discussed below)

    This constructs a 2-dimensional matrix of events and states. The compiler
    then will require the definition of state/event action functions. In the
    above example, the state/event actions are as follows:

    \code {.c}
    static void state_name1_event1_action(const event1_t* payload, state_id_t* state);
    static void state_name2_event1_action(const event1_t* payload, state_id_t* state);
    static void state_name3_event1_action(const event1_t* payload, state_id_t* state);
    static void state_name1_event2_action(const event2_t* payload, state_id_t* state);
    static void state_name2_event2_action(const event2_t* payload, state_id_t* state);
    static void state_name3_event2_action(const event2_t* payload, state_id_t* state);
    static void state_name1_event3_action(const event3_t* payload, state_id_t* state);
    static void state_name2_event3_action(const event3_t* payload, state_id_t* state);
    static void state_name3_event3_action(const event3_t* payload, state_id_t* state);
    \endcode

    The parameters for the action functions are:
        * payload - A pointer to the event payload
        * state - A pointer to the state variable. The possible values for the current
                  example are:
                    state_name1_state,
                    state_name2_state and
                    state_name3_state
                  The action function can write to this location to cause a state
                  transition.

    The state machine can be invoked as follows:
    \code {.c}
    event1_event payload;
    payload.arg = 1234;

    post_event1_event(&payload);
    \endcode

*/
/* enumeration of states */
#define STATE_ENUM(name)    name##_state,
typedef enum
{
    STATE_LIST(STATE_ENUM)
} state_id_t;

/* Current state */
static state_id_t current_state = INITIAL_STATE;

/* Definitions of the event payload data */
#define TYPEDEF_EVENT_PAYLOAD(name, payload, state)    typedef struct payload name##_t ;
EVENT_LIST(TYPEDEF_EVENT_PAYLOAD, ~)

/* Creates the prototypes for the state/event action functions */
#define STATE_EVENT_PROTOTYPE(name, unused, state) \
    static RETURN_TYPE state##_##name##_action(const name##_t*, state_id_t*);
#define STATE_EVENT_PROTOTYPE_GENERATOR(name)   EVENT_LIST(STATE_EVENT_PROTOTYPE, name)
STATE_LIST(STATE_EVENT_PROTOTYPE_GENERATOR)

/* Generate all the states

    A state is a struct with a set of function pointers for each event.
    This macro defines and generates the states.
*/
#define EVENT_ACTION(name, payload, state)      \
    RETURN_TYPE (*name##_action)(const name##_t*, state_id_t*);

#define EVENT_ACTION_REFERENCE(name, payload, state)    \
    state##_##name##_action,

#define STATE_INITIALISATION(name)                  \
    {                                               \
        EVENT_LIST(EVENT_ACTION_REFERENCE, name)    \
    },

static const struct state_t
{
    EVENT_LIST(EVENT_ACTION, ~)
} states[] =
{
    STATE_LIST(STATE_INITIALISATION)
};

/* Generate a set of functions to post events to the state machine

    These functions creates an event with the correct event id
*/
#define POST_EVENT_FUNCTION(name, __unused__, __fsm_internal_arg__)     \
static RETURN_TYPE post_##name##_event(const name##_t* payload)         \
{                                                                       \
    RETURN_TYPE rc;                                                     \
    rc = states[current_state].name##_action(payload, &current_state);  \
    return rc;                                                          \
}
EVENT_LIST(POST_EVENT_FUNCTION, ~)
